using System;
using System.Collections.Generic;
using PathX.Collection;
using UnityEngine;
using FPLibrary;

namespace PathX
{
    public interface IGrid
    {
        FPVector origin { get; }
        
        int sizeX { get; }
        
        int sizeZ { get; }
        
        Fix64 cellSize { get; }
        
        FPBB bounds { get; }
        
        IEnumerable<Cell> cells { get; }
        
        CellMatrix cellMatrix { get; }
        
        bool Contains(FPVector pos);
        
        Cell GetCell(FPVector position);
        
        Cell GetCell(FPVector position, bool adjustToBounds);

        Cell GetNeighbour(IGridCell cell, int dx, int dz);
        
        bool TryGetWalkableNeighbour(IGridCell cell, int dx, int dz, IUnitProperties unitProps, out Cell neighbour);
        
        IEnumerable<Cell> GetCellRange(FPVector position, Fix64 radius);

        void GetCellRange(FPVector position, Fix64 radius, ICollection<Cell> result);
        
        IEnumerable<Cell> GetCoveredCells(FPBB bounds);
        
        void GetCoveredCells(FPBB bounds, ICollection<Cell> result);
        
        void Apply(FPVector position, Fix64 radius, Action<Cell> act);
        
        void Apply(FPBB bounds, Action<Cell> act);
        
        IEnumerable<Cell> GetConcentricNeighbours(IGridCell center, int cellDistance);
        
        void GetConcentricNeighbours(IGridCell center, int cellDistance, DynamicArray<Cell> array);
        
        IEnumerator<IPathNode> GetWalkableNeighboursIterator(IGridCell c, IUnitProperties unitProps, bool excludeCornerCutting);
    }

    public class Grid : IGrid
    {
        private CellMatrix _cellMatrix;
        private FPVector _origin;
        private int _sizeX;
        private int _sizeZ;
        private Fix64 _cellSize;


        private Grid()
        {
            //_walkableIter = new WalkableNeighboursEnumerator(this);
        }
        
        public FPVector origin
        {
            get { return _origin; }
        }
        
        public int sizeX
        {
            get { return _sizeX; }
        }

        public int sizeZ
        {
            get { return _sizeZ; }
        }
        
        public Fix64 cellSize
        {
            get { return _cellSize; }
        }
        
        public FPBB bounds
        {
            get { return _cellMatrix.bounds; }
        }
        
        public IEnumerable<Cell> cells
        {
            get
            {
                return _cellMatrix.items;
            }
        }

        public CellMatrix cellMatrix
        {
            get
            {
                return _cellMatrix;
            }
        }

        public bool Contains(FPVector pos)
        {
            return cellMatrix.Contains(pos);
        }

        public Cell GetCell(FPVector position)
        {
            return cellMatrix.GetCell(position,false);
        }

        public Cell GetCell(FPVector position, bool adjustToBounds)
        {
            return cellMatrix.GetCell(position,adjustToBounds);
        }

        public Cell GetNeighbour(IGridCell cell, int dx, int dz)
        {
            var x = cell.matrixPosX + dx;
            var z = cell.matrixPosZ + dz;

            return _cellMatrix[x, z];
        }

        public bool TryGetWalkableNeighbour(IGridCell cell, int dx, int dz, IUnitProperties unitProps, out Cell neighbour)
        {
            var x = cell.matrixPosX + dx;
            var z = cell.matrixPosZ + dz;

            neighbour = _cellMatrix[x, z];

            if (neighbour == null)
            {
                return false;
            }

            return neighbour.IsWalkableFromWithClearance(cell, unitProps);
        }

        public IEnumerable<Cell> GetCellRange(FPVector position, Fix64 radius)
        {
            return GetCellRange(position, radius, radius, true);
        }

        public void GetCellRange(FPVector position, Fix64 radius, ICollection<Cell> result)
        { 
            GetCellRange(position, radius, radius, true, result);
        }
        
        private void GetCellRange(FPVector position, Fix64 radiusX, Fix64 radiusZ, bool requireCenterCell, ICollection<Cell> result)
        {
            Fix64 requiredOverlap = 0.0f;
            if (requireCenterCell)
            {
                requiredOverlap = _cellSize * 0.5f;
            }

            //We don't adjust to matrix bounds since that is also done in the call to GetRange
            var b = _cellMatrix.GetMatrixBounds(position, radiusX, radiusZ, requiredOverlap, false);
          //  _cellMatrix.GetRange(b, result);
        }
        private IEnumerable<Cell> GetCellRange(FPVector position, Fix64 radiusX, Fix64 radiusZ, bool requireCenterCell)
        {
            Fix64 requiredOverlap = 0.0f;
            if (requireCenterCell)
            {
                requiredOverlap = _cellSize * 0.5f;
            }

            //We don't adjust to matrix bounds since that is also done in the call to GetRange
            var b = _cellMatrix.GetMatrixBounds(position, radiusX, radiusZ, requiredOverlap, false);
            return default;
            //return _cellMatrix.GetRange(b);
        }

        public IEnumerable<Cell> GetCoveredCells(FPBB bounds)
        {
            throw new NotImplementedException();
        }

        public void GetCoveredCells(FPBB bounds, ICollection<Cell> result)
        {
            throw new NotImplementedException();
        }

        public void Apply(FPVector position, Fix64 radius, Action<Cell> act)
        {
            throw new NotImplementedException();
        }

        public void Apply(FPBB bounds, Action<Cell> act)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<Cell> GetConcentricNeighbours(IGridCell center, int cellDistance)
        {
            return _cellMatrix.GetConcentricNeighbours(center.matrixPosX, center.matrixPosZ, cellDistance);
        }

        public void GetConcentricNeighbours(IGridCell center, int cellDistance, DynamicArray<Cell> array)
        {
            _cellMatrix.GetConcentricNeighbours(center.matrixPosX, center.matrixPosZ, cellDistance, array);
        }

        public IEnumerator<IPathNode> GetWalkableNeighboursIterator(IGridCell c, IUnitProperties unitProps, bool excludeCornerCutting)
        {
            throw new NotImplementedException();
        }

        public static Grid Create(CellMatrix matrix)
        {
            var g = new Grid();

            g._cellMatrix = matrix;
            g._origin = matrix.origin;
            g._cellSize = matrix.cellSize;
            g._sizeX = matrix.columns;
            g._sizeZ = matrix.rows;

            return g;
        }
    }
}